package ca.swank.hq.ai;

import ca.swank.hardquor.*;

import java.util.LinkedList;
import java.util.TreeSet;
import java.util.Comparator;
import java.util.ArrayList;
import java.util.Iterator;

/**
* A version of the hardquorSimpleBoard that has additions for AI players
*
* @author Jesse Wilson
* @version 1.0 10/18/2001
*/
public class SmartBoard extends AIBoard
{
  public static final int WEIGHT_MAX = 1000;
  public static final int WEIGHT_MIN = -1000;

  public SmartBoard()
  { super();
  }

  public SmartBoard(SmartBoard other)
  { super(other);
  }

  public SmartBoard(int width, int height, int playerStartWalls)
  { super(width, height, playerStartWalls);
  }


  /**
   * gets an arraylist of all moves for player me on board board
   */
  public ArrayList getForwardWalkMoves(int[] source, int player)
  { ArrayList forwardMoves = new ArrayList();

    // set up distance board fore scoring moves
    int[][] distanceBoard = getDistanceHomeBoard(player);
    int currentDistance = distanceBoard[source[0]][source[1]];

    // add each move in order
    Iterator moves = getLegalMoves(source).iterator();
    while(moves.hasNext())
    { int[] target = (int[])moves.next();
      int newDistance = distanceBoard[target[0]][target[1]];
      if(newDistance < currentDistance)
      { forwardMoves.add(new WalkMove(source, target));
      }
    }

    return forwardMoves;
  }


  /**
   * gets an arraylist of all moves for player me on board board
   */
  public TreeSet getAllWalkMoves(int[] source, int player)
  { TreeSet processedCoordinates = new TreeSet(hardquorBoardTools.getCoordinateComparator());
    TreeSet unprocessedCoordinates = new TreeSet(hardquorBoardTools.getCoordinateComparator());
    TreeSet allWalkMovesHome = new TreeSet(new WalkMoveComparator());

    unprocessedCoordinates.add(source);

    while(!unprocessedCoordinates.isEmpty())
    { int[] start = (int[])unprocessedCoordinates.first();
      processedCoordinates.add(start);
      unprocessedCoordinates.remove(start);

      ArrayList movesFromHere = getForwardWalkMoves(start, player);
      for(int i = 0; i < movesFromHere.size(); i++)
      { allWalkMovesHome.add(movesFromHere.get(i));
        int[] target = ((WalkMove)movesFromHere.get(i)).getTarget();
        if(!processedCoordinates.contains(target)) unprocessedCoordinates.add(target);
      }
    }

    return allWalkMovesHome;
  }



  public ArrayList<WallMove> getAllDefensiveWalls(int[] source, int player, int threshHold, int[] wallValues)
  { ArrayList defences = new ArrayList();
    int scoreBefore = weighBoard(wallValues);

    ArrayList wallsWorthStopping = new ArrayList();
    ArrayList allWallsInMyWay = getAllBlockingWalls(source, player);
    for(int i = 0; i < allWallsInMyWay.size(); i++)
    { if(scoreBefore - weighMove((Move)allWallsInMyWay.get(i), wallValues) >= threshHold)
      { wallsWorthStopping.add(allWallsInMyWay.get(i));
      }
    }

    for(int i = 0; i < wallsWorthStopping.size(); i++)
    { WallMove defendable = (WallMove)wallsWorthStopping.get(i);

      // mutex defense walls
      makeMove(defendable);
      ArrayList allPotentialDefences = getAllBlockingWalls(source, player, hardquorUserGame.ERROR_PLAYER_BLOCKED);
      unMove(defendable);
      for(int j = 0; j < allPotentialDefences.size(); j++)
      { if(isLegalMove((WallMove)allPotentialDefences.get(j)) == hardquorUserGame.ERROR_NONE)
        { defences.add(allPotentialDefences.get(j));
        }
      }

      // criss-cross defense walls
      WallMove[] mutexWalls = defendable.getMutexWalls();
      for(int m = 0; m < mutexWalls.length; m++)
      { if(isLegalMove(mutexWalls[m]) == hardquorUserGame.ERROR_NONE) defences.add(mutexWalls[m]);
      }
    }

    return defences;
  }



  /**
   * gets an arraylist of all moves for player me on board board
   */
  public ArrayList<WallMove> getAllBlockingWalls(int[] source, int player)
  { return getAllBlockingWalls(source, player, hardquorUserGame.ERROR_NONE);
  }

  public ArrayList getAllBlockingWalls(int[] source, int player, int successErrorCode)
  {
    // convert to TreeSet
    ArrayList allBlockingWalls = new ArrayList();

    Iterator allWalkMoves = getAllWalkMoves(source, player).iterator();
    while(allWalkMoves.hasNext())
    { WalkMove walkMove = (WalkMove)allWalkMoves.next();
      WallMove[] walls = walkMove.getBlockingWalls();
      for(int i = 0; i < walls.length; i++)
      { if(isLegalMove(walls[i].a1(), walls[i].a2(), walls[i].b1(), walls[i].b2()) == successErrorCode)
        { allBlockingWalls.add(walls[i]);
        }
      }
    }
    return allBlockingWalls;
  }



  /**
   * weighs a move - the weight is simply the weight of the board after the move
   */
  public int weighMove(Move move, int[] wallValues)
  { int result = 0;

    makeMove(move);
    result = weighBoard(wallValues);
    unMove(move);

    return result;
  }

  /**
   * returns the enumerated integer of the player which is furthest from home.
   *
   * if the players are of equal distance, the return value is PLAYER_NONE.
   */
  public int getPlayerFurthestFromHome()
  {
    int playerDistance[] = new int[2];
    int[][] distanceBoard;
    int[] playerLoc;

    for(int player = 0; player < 2; player++)
    { distanceBoard = getDistanceHomeBoard(player);
      playerLoc  = getPlayerLocation(player);
      playerDistance[player] = distanceBoard[playerLoc[0]][playerLoc[1]];
    }

    if(playerDistance[PLAYER_ONE] > playerDistance[PLAYER_TWO]) return PLAYER_ONE;
    else if(playerDistance[PLAYER_TWO] > playerDistance[PLAYER_ONE]) return PLAYER_TWO;
    else return PLAYER_NONE;
  }



  /**
   * gets the current weight of the board
   */
  protected int weighBoard(int[] wallValues)
  { int[][][] distanceBoard = new int[2][][];
    int[][] playerLoc = new int[2][];
    int[] playerDistance = new int[2];
    int[] playerWalls = new int[2];
    int turn = getTurn();

    for(int p = 0; p < 2; p++)
    { distanceBoard[p] = getDistanceHomeBoard(p);
      playerLoc[p]  = getPlayerLocation(p);
      playerDistance[p] = distanceBoard[p][playerLoc[p][0]][playerLoc[p][1]];
      playerWalls[p] = getPlayerWalls(p);
    }

    // extreme cases:
    // game has been won
    if(playerDistance[0] == 0) return WEIGHT_MIN;
    else if(playerDistance[1] == 0) return WEIGHT_MAX;
    // winner is a step away
    else if(turn == 0 && playerDistance[0] == 1) return WEIGHT_MIN;
    else if(turn == 1 && playerDistance[1] == 1) return WEIGHT_MAX;
    // losing player has no walls, victory is secured for other player
    else if(playerWalls[1] == 0 && playerDistance[1] > playerDistance[0]) return WEIGHT_MIN;
    else if(playerWalls[0] == 0 && playerDistance[0] > playerDistance[1]) return WEIGHT_MAX;

    // player 0 - board score is greater, the further this player is away from home
    // i.e. higher score means player 0 losing
    int boardScore = 0;
    boardScore = boardScore + playerDistance[0];
    boardScore = boardScore - wallValues[playerWalls[0]];
    boardScore = boardScore - playerDistance[1];
    boardScore = boardScore + wallValues[playerWalls[1]];
    return boardScore;
  }

}